package d2;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.request.OapiProcessinstanceGetRequest;
import com.dingtalk.api.request.OapiProcessinstanceListidsRequest;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse.FormComponentValueVo;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse.OperationRecordsVo;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse.ProcessInstanceTopVo;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse.TaskTopVo;
import com.dingtalk.api.response.OapiProcessinstanceListidsResponse;
import com.dingtalk.api.response.OapiProcessinstanceListidsResponse.PageResult;

import cn.cybozu.api.KintoneRecord;
import cn.cybozu.api.KintoneSubTable;

public class D2WfConnector extends D2Connector
{
	// WF idを取得する最大数
	private static final long LIMIT_WF_SIZE = 10;
	private String processCode = null;
	
	public D2WfConnector(String processCode, String appKey, String appSecret, String domain, long appId, long spaceId, String apiToken)
	{
		super(appKey, appSecret, domain, appId, spaceId, apiToken);
		this.processCode = processCode;
	}
	/**
	 * 対象期間のinstane Idを取得
	 * @param startDate
	 * @param endDate
	 * @return
	 * @throws Exception
	 */
	private Map<String, Long> getInstanceIdsFromDingDing(Date startDate, Date endDate) throws Exception
	{
		Map<String, Long> recordMap = new HashMap<String, Long>();
		DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/processinstance/listids");
		OapiProcessinstanceListidsRequest  req = new OapiProcessinstanceListidsRequest();
		
		req.setProcessCode(processCode);
		req.setStartTime(startDate.getTime());
		req.setEndTime(endDate.getTime());
		req.setSize(LIMIT_WF_SIZE);
		req.setCursor(0L);
		
		for(;;)
		{
			
			OapiProcessinstanceListidsResponse resp = client.execute(req, getAccessToken());
			if(resp.getErrcode() != 0)
			{
				throw new Exception(resp.getErrmsg());
			}
			PageResult result = resp.getResult();
			
			for(String id : result.getList())
			{
				recordMap.put(id, null);
			}
			if(result.getNextCursor() == null)
			{
				break;
			}
			req.setCursor(result.getNextCursor());
		}
		return recordMap;
		
	}
	/**
	 * 期間指定でkintone上の申請recordを取得
	 * @param startDate
	 * @param endDate
	 * @return
	 * @throws Exception
	 */
	private List<KintoneRecord> getRecordsFromKintone(Date startDate, Date endDate) throws Exception
	{
		String query = "create_time >= \"" + formatDateTime(startDate) + "\" and create_time <= \"" + formatDateTime(endDate)  + "\" order by create_time asc";
		return getRecordsFromKintone(query, new String[] {"process_instance_id", "$id"});	
	}
	/**
	 * 対象instanceIdの申請をkintoneに移行
	 * @param instanceId
	 * @param kintoneId
	 * @throws Exception
	 */
	private void copyDingDing2Kintone(String instanceId, Long kintoneId)
		throws Exception
	{
		DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/processinstance/get");
		OapiProcessinstanceGetRequest req = new OapiProcessinstanceGetRequest();
		
		req.setProcessInstanceId(instanceId);
		OapiProcessinstanceGetResponse rep = client.execute(req, getAccessToken());
		if(rep.getErrcode() != 0)
		{
			throw new Exception(rep.getErrmsg());
		}
		
		ProcessInstanceTopVo vo = rep.getProcessInstance();
		
		KintoneRecord rec = new KintoneRecord();
		
		// 基本情報の設定
		rec.setString("process_instance_id", instanceId);
		rec.setString("business_id", vo.getBusinessId());
		rec.setString("title", vo.getTitle());
		rec.setString("status", vo.getStatus());
		rec.setString("result", vo.getResult());
		rec.setString("biz_action", vo.getBizAction());
		rec.setString("originator_userid", vo.getOriginatorUserid());
		rec.setString("originator_dept_id", vo.getOriginatorDeptId());
		rec.setString("originator_dept_name", vo.getOriginatorDeptName());
		List<String> ccs = vo.getCcUserids();
		rec.setString("cc_userids", ccs == null || ccs.size() == 0 ? null : String.join(",",ccs));
		rec.setString("create_time", formatDateTime(vo.getCreateTime()));
		rec.setString("finish_time", formatDateTime(vo.getFinishTime()));

		// 関連申請
		KintoneSubTable atRecs = new KintoneSubTable();
		List<String> atIds = vo.getAttachedProcessInstanceIds();
		if(atIds == null || atIds.size() == 0)
		{
			KintoneRecord subRec = new KintoneRecord();
			subRec.setString("attached_process_instance_id", null);
			atRecs.add(subRec);
		}
		else
		{
			for(String atId : atIds)
			{
				KintoneRecord subRec = new KintoneRecord();
				subRec.setString("attached_process_instance_id", atId);
				atRecs.add(subRec);
			}
		}
		rec.setSubTable("审批附属实例列表", atRecs);
		
		// 操作履歴
		KintoneSubTable opRecs = new KintoneSubTable();
		List<OperationRecordsVo> opes = vo.getOperationRecords();
		if(opes == null || opes.size() != 0)
		{
			for(OperationRecordsVo ope : opes)
			{
				KintoneRecord subRec = new KintoneRecord();
				subRec.setString("operation_userid", ope.getUserid());
				subRec.setString("operation_type", ope.getOperationType());
				subRec.setString("operation_result", ope.getOperationResult());
				subRec.setString("operation_remark", ope.getRemark());
				subRec.setString("operation_date", formatDateTime(ope.getDate()));	
				opRecs.add(subRec);
				
			}
		}
		else
		{
			KintoneRecord subRec = new KintoneRecord();
			subRec.setString("operation_userid", null);
			opRecs.add(subRec);
		}
		rec.setSubTable("操作记录列表", opRecs);
		
		// 承認履歴
		KintoneSubTable tsRecs = new KintoneSubTable();
		List<TaskTopVo> tasks = vo.getTasks();
		if(tasks == null || tasks.size() != 0)
		{
			for(TaskTopVo task : tasks)
			{
				KintoneRecord subRec = new KintoneRecord();
				subRec.setString("task_userid", task.getUserid());
				subRec.setString("task_status", task.getTaskStatus());
				subRec.setString("task_result", task.getTaskResult());
				subRec.setString("taskid", task.getTaskid());
				subRec.setString("task_create_time", formatDateTime(task.getCreateTime()));
				subRec.setString("task_finish_time", formatDateTime(task.getFinishTime()));
				tsRecs.add(subRec);
			}
		}
		else
		{
			KintoneRecord subRec = new KintoneRecord();
			subRec.setString("task_userid", null);
			tsRecs.add(subRec);
		}
		rec.setSubTable("已审批任务列表", tsRecs);
		
		
		// カスタマイズ項目
		List<FormComponentValueVo> values = vo.getFormComponentValues();
		KintoneSubTable subTable = new KintoneSubTable();
		
		for(FormComponentValueVo v: values)
		{
			KintoneRecord subRec = new KintoneRecord();
			subRec.setString("field_name", v.getName());
			subRec.setString("field_value", v.getValue());
			subRec.setString("field_ext_value", v.getExtValue());
		}
		rec.setSubTable("fields", subTable);
		
		if(kintoneId != null)
		{
			rec.setId(kintoneId);
			putRecordToKintone(rec);
		}
		else
		{
			postRecordToKintone(rec);
		}
	}
	/**
	 * 期間指定での移行
	 * @param startDate 開始日
	 * @param endDate 終了日
	 * @throws Exception
	 */
	public void execute(Date startDate, Date endDate) throws Exception
	{
		// 終了日対象日の23:59:59までに
		endDate = new Date(endDate.getTime() +  + TimeUnit.DAYS.toMillis(1) - 1);
		
		// 钉钉から対象期間のinstance IDを取得
		Map<String, Long> recordMap = getInstanceIdsFromDingDing(startDate, endDate);
		
		// kintoneから対象期間の連携済みrecordを取得
		List<KintoneRecord> recs = getRecordsFromKintone(startDate, endDate);
		
		for(KintoneRecord rec : recs)
		{
			String processInstanceId = rec.getString("process_instance_id");
			// 連携済みの場合は、更新
			copyDingDing2Kintone(processInstanceId, rec.getId());
			recordMap.remove(processInstanceId);
		}
		// 新規追加
		for(String processInstanceId: recordMap.keySet())
		{
			copyDingDing2Kintone(processInstanceId, null);
		}
	}
	
	public static void main(String[] args)
		throws Exception
	{
		
		//add
		//------------------------------------------------------------------------------------------
		String date[] ={"2019-07-01","2019-07-30"};
		String processCode = "xxxxxxxxxxx";


		String appKey = "xxxxxxxxxxx";
		String appSecret = "xxxxxxxxxxx";
		String domain = "xxxxxxxxxxx.cybozu.cn";
		long appId = 117;
		long spaceId = 0;
		String apiToken = "xxxxxxxxxxx";
		//-------------------------------------------------------------------------------------------
		
//		if(args.length < 3)
//		{
//			System.err.println("args[0]: start date(yyyy-mm-dd). args[1]: end date(yyyy-mm-dd). args[2]:process code. ");
//			System.exit(-1);
//		}
		
		//modify
		(new D2WfConnector(processCode, appKey, appSecret, domain, appId, spaceId, apiToken))
		.execute(D2Connector.parseDate(date[0]), D2Connector.parseDate(date[1]));
		
		System.out.println("success");
		
//		(new D2WfConnector(args[2], "123456", "1234567890123456789012345678901234567890123", "test.cybozu.cn", 1, 0, "api_token-xxxxx"))
//			.execute(D2Connector.parseDate(args[0]), D2Connector.parseDate(args[1]));
	}
}
